package com.sgnb;


import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;

/**
 * @author mojie126
 * @date 2020-02-19 15:40
 */
public class printBinName extends JFrame {

	private static final long serialVersionUID = 2L;
	//	URLClassLoader classLoader = null;
	File[] file;
	JButton btn;
	TextArea jarField = new TextArea(5, 45);
	JButton run;
	JTextField runFunc;
	TextArea textArea = new TextArea(10, 78);
	ArrayList<String> jarFileList = new ArrayList<>();
	StringBuffer classPath = new StringBuffer();
	StringBuffer sourcePath = new StringBuffer();

	public static void main(String[] args) {
		new printBinName();
	}

	public printBinName() {
		this.setTitle("SGNB Jar Run v" + serialVersionUID);

		FlowLayout layout = new FlowLayout();// 布局
		JLabel selectJar = new JLabel("请选择文件：");// 标签
		JLabel inputRunFunc = new JLabel("运行的方法：");// 标签
		runFunc = new JTextField(30);// 文本域
		btn = new JButton("浏览");// 按钮
		run = new JButton("运行");// 按钮

		// 设置布局
		layout.setAlignment(FlowLayout.LEFT);// 左对齐
		this.setLayout(layout);
		this.setBounds(400, 200, 600, 400);
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JPanel panel1 = new JPanel();
		panel1.add(selectJar);
		panel1.add(jarField);
		jarField.setEditable(false);
		jarField.setBackground(Color.BLACK);
		jarField.setForeground(Color.WHITE);
		btn.addActionListener(new btn());
		panel1.add(btn);
		this.add(panel1);

		JPanel panel2 = new JPanel();
		panel2.add(inputRunFunc);
		panel2.add(runFunc);
		run.addActionListener(new run());
		panel2.add(run);
		this.add(panel2);

		JPanel panel3 = new JPanel();
		textArea.setEditable(false);
		textArea.setBackground(Color.BLACK);
		textArea.setForeground(Color.WHITE);
		panel3.add(textArea);
		this.add(panel3);

		this.getRootPane().setDefaultButton(run);

	}

	class btn implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			System.gc();
			JFileChooser chooser = new JFileChooser();
			//注意了，这里重要的一句
			FileSystemView fsv = FileSystemView.getFileSystemView();
			//默认路径是桌面
			chooser.setCurrentDirectory(fsv.getHomeDirectory());
			//只允许选择文件
			chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
			//多选
			chooser.setMultiSelectionEnabled(true);
			//设置文件过滤器
			chooser.setFileFilter(new FileNameExtensionFilter("请选择指定文件 (jar，zip，java，class)", "jar", "zip", "java", "class"));
			//获取用户的操作结果，是确认了，还是取消了
			int choose = chooser.showOpenDialog(null);
//			chooser.showDialog(new JLabel(), "选择");
			//判断选择的结果
			if (choose == JFileChooser.APPROVE_OPTION) {
				file = chooser.getSelectedFiles();
				StringBuilder stringBuilder = new StringBuilder();
				for (File f : file) {
					String file_path = f.getAbsoluteFile().toString();
					stringBuilder.append(file_path).append("\n");
					jarFileList.add(file_path);
					loadJar(file_path);
					if (file_path.endsWith(".jar") || file_path.endsWith(".zip") || file_path.endsWith(".class")) {
						classPath.append(file_path).append(";");
					} else if (file_path.endsWith(".java")) {
						sourcePath.append(file_path).append(";");
					}
				}
				jarField.setText(stringBuilder.toString());
			}
		}
	}

	class run implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			//获取文本框中的文本
			String tf_str = runFunc.getText().trim();
			try {
				final String clazzName = tf_str.substring(0, tf_str.lastIndexOf("."));
				final String funcName = tf_str.substring(tf_str.lastIndexOf(".") + 1);
				MyClassLoader myClassLoader = new MyClassLoader();
				Class<?> clazz = null;
				for (File f : file) {
					String file_path = f.getAbsoluteFile().toString();
					System.out.println(file_path);
					if (file_path.endsWith(".java")) {
						loadJar(file_path);
					}
					try {
						clazz = Class.forName(clazzName);
					} catch (ClassNotFoundException ignore) {
						if (file_path.endsWith(".java")) {
							clazz = myClassLoader.loadClass(clazzName, new File(file_path.replace(".java", ".class")));
						} else if (file_path.endsWith(".class")) {
							clazz = myClassLoader.loadClass(clazzName, new File(file_path));
						} else if (file_path.endsWith(".jar") || file_path.endsWith(".zip")) {
							try {
								clazz = Class.forName(clazzName);
							} catch (ClassNotFoundException ignored) {
							}
						}
					} finally {
						boolean isDelete = new File(file_path.replace(".java", ".class")).delete();
						System.out.println(isDelete);
					}
				}
				assert clazz != null;
				Method m = clazz.getDeclaredMethod(funcName);
				textArea.append("执行方法：" + clazzName + "." + funcName + "\n");
				textArea.append("执行结果：" + m.invoke(clazz.newInstance()) + "\n");
			} catch (IllegalAccessException | InstantiationException | IOException ex) {
				textArea.append(ex + "\n");
				ex.printStackTrace();
			} catch (InvocationTargetException ignore) {
				textArea.append("错误：调用方法 " + tf_str + " 失败\n");
			} catch (NullPointerException ignore) {
				textArea.append("错误：请选择要运行的Jar文件\n");
			} catch (ArrayIndexOutOfBoundsException ignore) {
				textArea.append("错误：请输入要运行的方法，例如：ma.b\n");
			} catch (NoSuchMethodException | StringIndexOutOfBoundsException ignore) {
				textArea.append("错误：选择的文件中没有该方法\n");
			} finally {
				textArea.append("\n执行完成\n");
				textArea.append("\n---------------------------------------------------------------------------------------------------------------\n");
				System.gc();
			}
		}
	}

	private void loadJar(String jarPath) {
		// 从URLClassLoader类中获取类所在文件夹的方法，jar也可以认为是一个文件夹
		File jarFile = new File("");
		if (jarPath.endsWith(".java")) {
			complierAndRun(jarPath);
		} else {
			jarFile = new File(jarPath);
		}

		if (!jarPath.endsWith(".jar") && !jarPath.endsWith(".zip")) {
			return;
		}

		if (!jarFile.exists()) {
			textArea.append("错误：Jar文件未找到\n");
			textArea.append("\n---------------------------------------------------------------------------------------------------------------\n");
			return;
		}

		//获取类加载器的addURL方法，准备动态调用
		Method method = null;
		try {
			method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
		} catch (NoSuchMethodException | SecurityException e1) {
			e1.printStackTrace();
		}

		// 获取方法的访问权限，保存原始值
		assert method != null;
		boolean accessible = method.isAccessible();
		try {
			//修改访问权限为可写
			if (!accessible) {
				method.setAccessible(true);
			}

			// 获取系统类加载器
			URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();

			//获取jar文件的url路径
			URL url = jarFile.toURI().toURL();

			//jar路径加入到系统url路径里
			method.invoke(classLoader, url);

		} catch (IllegalAccessException | InvocationTargetException | MalformedURLException e) {
			e.printStackTrace();
		} finally {
			//回写访问权限
			method.setAccessible(accessible);
			System.gc();
		}

	}

	public void complierAndRun(String file) {
		try {
			//动态编译
			JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
			int status;
//			javac.run(null, null, null, "-version");
//			javac.run(null, null, null, "-help");
			if (classPath.toString().equals("")) {
				status = javac.run(null, null, null,
						"-source", "1.3", "-target", "1.3", "-g", "-O", "-verbose", "-nowarn", file
				);
			} else {
				status = javac.run(null, null, null,
						"-source", "1.3", "-target", "1.3", "-g", "-O", "-verbose", "-nowarn", "-classpath", classPath.toString(), file
				);
			}
			System.out.println(status);
			if (status != 0) {
				textArea.append("没有编译成功\n");
				textArea.append("\n---------------------------------------------------------------------------------------------------------------\n");
			}
		} catch (Exception ignored) {
		}
	}

	static class MyClassLoader extends ClassLoader {
		public synchronized Class<?> loadClass(String name, File file) throws FileNotFoundException {
			Class<?> cls = findLoadedClass(name);
			if (cls != null) {
				return cls;
			}
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len;
			try {
				while (true) {
					len = fis.read(buffer);
					if (len == -1) {
						break;
					}
					baos.write(buffer, 0, len);
				}
				//FileInputStream的flush是空操作，因为flush的作用是把缓存中的东西写入实体(硬盘或网络流)中，这里没有这种必要所以为空
				//baos.flush();
				byte[] data = baos.toByteArray();
				return defineClass(null, data, 0, data.length);
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					baos.close();
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return null;
		}
	}

}
